package server;

import java.io.*;
import java.net.*;
import java.util.Vector;

/**
 * this class handles user's send message, register, login
 * @author kaidix
 *
 */
public class IOService implements Runnable {

	Socket socket,socket1;
	private ObjectInputStream in;
	private ObjectOutputStream out;
	private database.MyIMDB db;
	boolean run = true;
	public IOService(Socket s,Socket s1,database.MyIMDB db){
		socket = s;
		socket1 = s1;
		this.db = db;
	}

	@Override
	public void run() {
//		System.out.println("something");
		// TODO Auto-generated method stub
		try{
//			System.out.println("something2");
			try{
				out = new ObjectOutputStream(socket.getOutputStream());
//				System.out.println("something1");
				in = new ObjectInputStream(socket.getInputStream());
				doService();
			}
			catch(Exception e){
				e.printStackTrace();
			}
			finally{
				socket.close();
			}
		}
		catch(IOException e){
			e.printStackTrace();
		}
	}

/**
 * the main loop
 * @throws ClassNotFoundException
 */
	private void doService() throws  ClassNotFoundException{
		String name="";
		boolean connected = false;
		data.Message m;
		//before logged in
		while(!connected){
			try {
				m = (data.Message)in.readObject();


				switch(m.getType()){
				case data.Information.MESSAGE_TYPE_LOGIN:
					if(login(m)){
						name = ((data.UserInfo)m.getContent()).getUserName();
						connected = true;
					}

					break;
				case data.Information.MESSAGE_TYPE_REGIST:
					register(m);

					break;

				}
			} catch (IOException e) {
				// TODO Auto-generated catch block

				try {
					socket.close();

					socket1.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				System.out.println("connection closed before login");
				run=false;





				//e.printStackTrace();
				break;
			}
		}

//		System.out.println("ccc  "+run);
		// after logged in
		if(run){
			System.out.println("connected ip is" + socket.getInetAddress());
			sendMessage s = new sendMessage(socket1,name,db);
			Thread t = new Thread(s);
			t.start();

//			System.out.println("aaaaaaaaaa");
			//after login
			while(run){
//				System.out.println("bbbbbbbbb");
				String friend;
				String message;

				try {
					m = (data.Message)in.readObject();

					switch(m.getType()){
					case data.Information.MESSAGE_TYPE_ADD_FRIEND:
						friend = ((String)m.getContent());
						addFriend(name,friend);
						break;
					case data.Information.MESSAGE_TYPE_DELETE_FRIEND:
						friend = ((String)m.getContent());
						deleteFriend(name,friend);
						break;				
					case data.Information.MESSAGE_TYPE_SENT_TO:
						friend =  ((data.TextMsg)m.getContent()).getTo();
						message = ((data.TextMsg)m.getContent()).getContent();
						sendTo(name,friend,message);
						break;		
					case data.Information.MESSAGE_TYPE_USER_INFO:
						getUserInfo(name);
						break;	
					case data.Information.MESSAGE_TYPE_GROUP_ADD:
						groupAdd(name,(String)m.getContent());
						break;
					case data.Information.MESSAGE_TYPE_GROUP_SENT:
						groupSendMsg((data.TextMsg)m.getContent());
						break;
					case data.Information.MESSAGE_TYPE_GROUP_LIST:
						
						groupGetList(name);
						break;
					case -1:
						run = false;
						s.stop();
						break;
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					s.stop();
					try {
						System.out.println("connection closed after login");
						socket.close();

						socket1.close();
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					break;
				}
			}
		}
	}



	//----------------------------------------------login/reg-----------------------------------------//
	/**
	 * login orperation
	 */
	private boolean login(data.Message m) throws IOException{
		if(db.login(((data.UserInfo)m.getContent()).getUserName(), ((data.UserInfo)m.getContent()).getPassword())==true){
			System.out.println("login ack");
			data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ACK,null);
			out.writeObject(m1);
			return true;
		}
		else{
			System.out.println("login err");
			data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ERR,null);
			out.writeObject(m1);
			return false;
		}
	}
	/**
	 * the register new account operation
	 * @param m the packet continent
	 * @throws IOException
	 */
	private void register(data.Message m) throws IOException{
		if(db.addUser(((data.UserInfo)m.getContent()).getUserName(), ((data.UserInfo)m.getContent()).getPassword())==true){
			System.out.println("reg success");
			data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ACK,null);
			out.writeObject(m1);
		}
		else{
			System.out.println("reg fail");
			data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ERR,null);
			out.writeObject(m1);
		}
	}



	//----------------------------------------service----non group--------------------------------------------//
	/**
	 * add friend operation
	 */
	private void addFriend(String name, String friend) throws IOException{
		if(db.addFriend(name, friend)==1){
			data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ACK,null);
			out.writeObject(m1);
			out.flush();
		}
		else{
			data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ERR,null);
			out.writeObject(m1);
			out.flush();
		}
	}
	/**
	 * remove friend operation
	 * @param name the current logged in user's name
	 * @param friend the friend's name
	 * @throws IOException
	 */
	private void deleteFriend(String name, String friend) throws IOException{
		db.removeFriend(name, friend);
		data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ACK,null);
		out.writeObject(m1);
		out.flush();

	}
	/**
	 * get user's friend list
	 * @param name the logged in user
	 * @throws IOException
	 */
	private void getUserInfo(String name) throws IOException{
		Vector<String> a = db.getFriendList(name);
		data.FriendList f = new data.FriendList();
		for(int i = 0; i < a.size();i++){
			f.addfriend(a.elementAt(i));
		}
		data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_USER_INFO,f);
		out.writeObject(m1);
		out.flush();
	}
	/**
	 * the send message operation
	 * @param name sender
	 * @param to receiver
	 * @param message text
	 * @throws IOException
	 */
	private void sendTo(String name, String to, String message) throws IOException{
		db.leaveMessage(name, to, message);
		data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ACK,null);
		out.writeObject(m1);
		out.flush();
	}
	
	//--------------------------group-------------------------------------//
	/**
	 * member join a group
	 */
	private void groupAdd(String name, String group) throws IOException{
		db.createGroup(name, group);
		data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ACK,null);
		out.writeObject(m1);
		out.flush();
	}
	/**
	 * send a group message
	 * @param m the continent of the packet
	 * @throws IOException
	 */
	private void groupSendMsg(data.TextMsg m) throws IOException{
		db.leaveGroupMessage(m.getFrom(), m.getTo(), m.getContent());
		System.out.println("|"+m.getFrom()+"|"+ m.getTo()+"|"+m.getContent()+"|");
		data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_ACK,null);
		out.writeObject(m1);
		out.flush();
	}
	/**
	 * get group list
	 * @param name the user's name
	 * @throws IOException
	 */
	private void groupGetList(String name) throws IOException{
		Vector<String> a = db.getGroup(name);
		data.FriendList f = new data.FriendList();
		for(int i = 0; i < a.size();i++){
			f.addfriend(a.elementAt(i));
		}
		data.Message m1 = new data.Message(data.Information.MESSAGE_TYPE_GROUP_LIST,f);
		out.writeObject(m1);
		out.flush();
	}
}
